package bo;

import util.CustomException;
import factory.HibernateUtil;
import java.io.Serializable;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

public abstract class AbstractBO implements Serializable {

    private Session session;

    /**
     * Retorna a sessão com o banco de dados.
     *
     * @return Session
     * @throws CustomException
     */
    protected Session getSession() throws CustomException {
        if ((session == null) || (!(session.isOpen()))) {
            try {
                SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
                session = sessionFactory.openSession();
            } catch (CustomException e) {
                throw e;
            } catch (Exception e) {
                throw new CustomException("Falha ao abrir sessão com o banco de dados.", e);
            }
        }
        return session;
    }

    /**
     * Fecha a sessão com o banco de dados.
     *
     * @throws CustomException
     */
    private void closeSession() throws CustomException {
        try {
            getSession().close();
        } catch (CustomException e) {
            throw e;
        } catch (Exception e) {
            throw new CustomException("Falha ao fechar sessão com o banco de dados.", e);
        }
    }

    /**
     * Retorna a transação com o banco de dados.
     *
     * @return Transaction
     * @throws CustomException
     */
    private Transaction getTransaction() throws CustomException {
        try {
            return getSession().getTransaction();
        } catch (CustomException e) {
            throw e;
        } catch (Exception e) {
            throw new CustomException("Falha ao obter transação com o banco de dados.", e);
        }
    }

    /**
     * Inicia a transação com o banco de dados.
     *
     * @throws CustomException
     */
    protected void beginTransaction() throws CustomException {
        try {
            if (!inTransaction()) {
                getTransaction().begin();
            }
        } catch (CustomException e) {
            throw e;
        } catch (Exception e) {
            throw new CustomException("Falha ao iniciar transação com o banco de dados.", e);
        }
    }

    /**
     * Faz rollback na transação com o banco de dados.
     *
     * @throws CustomException
     */
    protected void rollbackTransaction() throws CustomException {
        try {
            if (inTransaction()) {
                getTransaction().rollback();
            }
        } catch (CustomException e) {
            throw e;
        } catch (Exception e) {
            throw new CustomException("Falha ao fazer rollback na transação com o banco de dados.", e);
        } finally {
            closeSession();
        }
    }

    /**
     * Faz commit na transação com o banco de dados.
     *
     * @throws CustomException
     */
    protected void commitTransaction() throws CustomException {
        try {
            if (inTransaction()) {
                getTransaction().commit();
            }
        } catch (CustomException e) {
            throw e;
        } catch (Exception e) {
            throw new CustomException("Falha ao fazer commit na transação com o banco de dados.", e);
        } finally {
            closeSession();
        }
    }

    /**
     * Retorna se a transação está aberta ou não.
     *
     * @return boolean
     * @throws CustomException
     */
    protected boolean inTransaction() throws CustomException {
        try {
            return (getTransaction().isActive());
        } catch (CustomException e) {
            throw e;
        } catch (Exception e) {
            throw new CustomException("Falha ao verificar se a transação está ativa.", e);
        }
    }
}
